/*
 * Copyright 2011-2012 INSA Rennes
 *
 * This file is part of EIImage.
 *
 * EIImage is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * EIImage is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with EIImage.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "GenericInterface.h"
#include "DoubleImageWindow.h"
#include "GridView.h"
#include <QDoubleSpinBox>
#include <QSlider>

using namespace genericinterface;
using namespace imagein;
using namespace std;

DoubleImageWindow::DoubleImageWindow(Image_t<double>* image, const QString path, bool normalize, bool logScale, double logConstantScale, bool abs)
    : ImageWindow(path), _image(image)
{
    _image = image;

    _normalize=normalize;
    _logScale=logScale;
    _logConstantScale=logConstantScale;
    _abs=abs;

    this->setDisplayImage();

    this->setWindowTitle(ImageWindow::getTitleFromPath(path));

    init();
}

DoubleImageWindow::DoubleImageWindow(const DoubleImageWindow& siw, imagein::Image_t<double>* image)
    : ImageWindow(siw.getPath())
{
    if(image == NULL) {
        image = new Image_t<double>(*siw._image);
    }

    _normalize=siw._normalize;
    _logScale=siw._logScale;
    _logConstantScale=siw._logConstantScale;

    _image = image;
    this->setDisplayImage(this->CONS);

    this->setWindowTitle(siw.windowTitle());

    init();
}

DoubleImageWindow::~DoubleImageWindow()
{
    delete _image;
    delete _displayImg;
}


void DoubleImageWindow::init()
{
    QObject::connect(this->view(), SIGNAL(updateSrc(GenericHistogramView*,imagein::Rectangle)), this, SLOT(updateSrc(GenericHistogramView*,imagein::Rectangle)));

    menu()->addAction(tr("Pixels Grid"), this, SLOT(showPixelsGrid()));
    menu()->addSeparator();
    menu()->addAction(tr("Crop"), this, SLOT(crop()));
    menu()->addAction(tr("Copy & crop"), this, SLOT(copycrop()));


    updateStatusBar();

}

void DoubleImageWindow::updateSpecStatusBar(){
    typeoftheimage->setText(QString(tr("Real")));

    if(_logScale) {
            QWidget* logWidget = new QWidget();
            QHBoxLayout* logLayout = new QHBoxLayout(logWidget);
            QSlider* logSlider = new QSlider(Qt::Horizontal);
            logSlider->setRange(0, 12);
            logSlider->setValue( (log2(_logConstantScale) / 3. + 3.) * 2.);
        //    QDoubleSpinBox* logBox = new QDoubleSpinBox();
        //    logBox->setRange(0., 1000000000.);
        //    logBox->setValue(_logConstant);
            QObject::connect(logSlider, SIGNAL(valueChanged(int)), this, SLOT(setLogScale(int)));
            logLayout->addWidget(new QLabel(tr("Log scale : ")));

            logLayout->addWidget(logSlider);
            _infoLayout->addWidget(logWidget);
    }



    QString stats(tr("min : %1\t max : %2\t mean : %3\t standard deviation : %4"));
           QString min="", max="", mean="", dev="";
           for(unsigned int c = 0; c < _image->getNbChannels(); ++c) {
               min += QString("%1").arg(_image->min(c));
               max += QString("%1").arg(_image->max(c));
               mean += QString("%1").arg(_image->mean(c), 0, 'f', 1);
               dev += QString("%1").arg(_image->deviation(c), 0, 'f', 1);
               if(c < _image->getNbChannels()-1)  {
                   min+=" "; max+=" "; mean+=" "; dev+=" ";
               }
           }
           stats = stats.arg(min).arg(max).arg(mean).arg(dev);
           _lStats->setText(stats);

}


void DoubleImageWindow::showPixelsGrid()
{
    GridView* grid = new GridView(_image, _displayImg);
    grid->setWindowTitle(this->windowTitle() + QString(" - ")  + tr("Pixels Grid"));
    emit addWidget(this, grid);
}

void DoubleImageWindow::crop() {
    const Image_t<double>* oldImg = _image;
    const Image* oldDisplayImg = _displayImg;
    Image_t<double>* newImg = oldImg->crop(_imageView->getRectangle());
    _image = newImg;
    this->setDisplayImage(this->CONS);
    delete oldImg;
    delete oldDisplayImg;

    view()->update();
    this->adjustSize();
    this->updateGeometry();
}

void DoubleImageWindow::copycrop() {
    Image_t<double>* newImg = _image->crop(_imageView->getRectangle());
    DoubleImageWindow* newImgWnd = new DoubleImageWindow(*this, newImg);
    emit addImage(this, newImgWnd);
}


void DoubleImageWindow::showSelectedPixelInformations(int x, int y) const
{
    showPixelInformations(x, y, _lSelectedPixelPosition,_lSelectedPixelRGBColor,_lSelectedPixelHSVColor,_lSelectedPixelHSLColor);
}

void DoubleImageWindow::showHoveredPixelInformations(int x, int y) const
{
    showPixelInformations(x, y, _lHoveredPixelPosition,_lHoveredPixelRGBColor,_lHoveredPixelHSVColor,_lHoveredPixelHSLColor);
}

void DoubleImageWindow::showPixelInformations(int x, int y, QLabel *labelpos,QLabel *labelrgb,QLabel *labelhsv,QLabel *labelhsl) const{
    labelpos->setText(QString("%1x%2").arg(x).arg(y));
    unsigned char red=0, green=0,blue=0;

    for(unsigned int i = 0; i < _image->getNbChannels(); i++){
        try {
            unsigned char temp = _displayImg->getPixel(x, y, i);
            switch(i){
            case 0 : red   = temp;break;
            case 1 : green = temp;break;
            case 2 : blue  = temp;break;
            }
             }
             catch(std::out_of_range&) {
        }
    }

    //The value in RGB
    QColor RGB = QColor::fromRgb(red,green,blue);
    labelrgb->setText(QString(tr(" (RGB) %1 %2 %3")).arg(RGB.red(),3,'f',0,' ').arg(RGB.green(),3,'f',0,' ').arg(RGB.blue(),3,'f',0,' ') );

    //the HSL color depend on RGB
    QColor HSV = RGB.toHsv();
    labelhsv->setText(QString(tr(" (HSV) %1 %2 %3")).arg(HSV.hue(),3,'f',0,' ').arg(HSV.saturation(),3,'f',0,' ').arg(HSV.value(),3,'f',0,' ') );

    //the HSL color depend on RGB
    QColor HSL = RGB.toHsl();
    labelhsl->setText(QString(tr(" (HSL) %1 %2 %3")).arg(HSL.hue(),3,'f',0,' ').arg(HSL.saturation(),3,'f',0,' ').arg(HSL.lightness(),3,'f',0,' ') );

}

void DoubleImageWindow::updateSrc(GenericHistogramView* /*histo*/, imagein::Rectangle /*rect*/) {
}

Image* DoubleImageWindow::makeDispImage(Mode mode) {
    Image * image = new Image();
    if(mode == this->CONS){
        image = Converter<Image_t<double> >::makeDisplayable(_image,NULL);
    }else if(mode == this->USER){
        image =  Converter<Image_t<double> >::makeDisplayable(_image,_param);
    }
    return image;
    /**
    Image_t<double>* tmpImg = new Image_t<double>(*image);
    Image* resImg = new Image(image->getWidth(), image->getHeight(), image->getNbChannels());

    if(_abs) {
        for(Image_t<double>::iterator it = tmpImg->begin(); it < tmpImg->end(); ++it) {
            *it = std::abs(*it);
        }
    }
    std::cout << "Before normalize : " << tmpImg->min() << ":" << tmpImg->max() << std::endl;
    if(_normalize) {
        tmpImg->normalize(0.0, 255.0);
    }

    std::cout << "After normalize : " << tmpImg->min() << ":" << tmpImg->max() << std::endl;
    double mean = tmpImg->mean();
    double logConstant = exp(-log2(mean)) / 8.;
    std::cout << "Log constant scale = " << _logConstantScale << std::endl;
    std::cout << "Log constant = " << logConstant << std::endl;
    for(unsigned int c = 0; c < image->getNbChannels(); ++c) {
        const double denom = log(255.0 * logConstant * _logConstantScale + 1.0);
        const double factor = 255.0 / denom;
        for(unsigned int j = 0; j < image->getHeight(); ++j) {
            for(unsigned int i = 0; i < image->getWidth(); ++i) {
                double mag = tmpImg->getPixel(i, j, c);
                if(_logScale) {
                    mag = log(mag*logConstant*_logConstantScale + 1.0) * factor;
                }
                mag = std::min(255.0, std::max(0.0, mag));
                resImg->setPixel(i, j, c, mag);
            }
        }
    }
    return resImg;
    **/
}

void DoubleImageWindow::setLogScale(int logScale) {

//    _logConstant = logConstant;
    const Image* tmpImg = _displayImg;
    _logConstantScale = std::pow(8, logScale/2. - 3.);
    this->setDisplayImage(this->CONS);
    delete tmpImg;
}

void DoubleImageWindow::setMemoryImage(Image_t<double> * image){
    _image = image;
    this->setDisplayImage(this->USER);
}


